﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;

namespace Communication
{
    public class AsyncReceiver //: IAsyncCommunication
    {
        TcpListener tcpListener;

        private static ManualResetEvent allDone = new ManualResetEvent(false);
        public event TickHandler newMessage;
        public delegate void TickHandler(AsyncReceiver aReceiver, MessageEvent e);

        public AsyncReceiver(IPEndPoint endPoint)
        {
            tcpListener = new TcpListener(endPoint);
            Console.WriteLine("<Receiver>Receiver activated!");
            Thread listenThread = new Thread(new ThreadStart(ListenForClients));
            listenThread.Start();
        }

        private void ListenForClients()
        {
            try
            {
                this.tcpListener.Start();

                while (true)
                {
                    // Set the event to nonsignaled state.
                    allDone.Reset();

                    // Start an asynchronous socket to listen for connections.
                    Console.WriteLine("<Receiver>Waiting for a connection...");

                    tcpListener.BeginAcceptTcpClient(
                        new AsyncCallback(ReceiveCallback),
                        tcpListener.Server);

                    // Wait until a connection is made before continuing.
                    allDone.WaitOne();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("<Receiver>Error while connecting socket." + e.ToString());
            }
        }

        void ReceiveCallback(IAsyncResult ar)
        {
            allDone.Set();

            // Create the state object.
            StateObject state = new StateObject();
            state.workSocket = (Socket)(ar.AsyncState);
            state.workSocket = state.workSocket.EndAccept(ar);
            Console.WriteLine("<Receiver>From: " + state.workSocket.RemoteEndPoint.ToString());
            state.workSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReadCallback), state);
        }

        public void ReadCallback(IAsyncResult ar)
        {
            String content = String.Empty;

            // Retrieve the state object and the handler socket
            // from the asynchronous state object.
            StateObject state = (StateObject)ar.AsyncState;
            //Socket handler = state.workSocket;
            int bytesRead = 0;
            try
            {
                // Read data from the client socket. 
                bytesRead = state.workSocket.EndReceive(ar);
                //Console.WriteLine("<Receiver>Bytes read: " + bytesRead.ToString());
            }
            catch (SocketException ex)
            {
                Console.WriteLine("<Receiver>While recieved message, the socket has been forecefully closed.\n" + ex);
            }
            
            if (bytesRead > 0)
            {
                // There  might be more data, so store the data received so far.
                state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

                // Check for end-of-file tag. If it is not there, read more data.
                content = state.sb.ToString();
                if (content.EndsWith("<EOF>"))
                {
                    // All the data has been read from the 
                    // client. Display it on the console.
                    //Console.WriteLine("<Receiver>Received:" + content);
                    this.newMessage(this, new MessageEvent(content,((IPEndPoint)state.workSocket.RemoteEndPoint).Address));
                }
                else
                {
                    // Not all data received. Get more.
                    state.workSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReadCallback), state);
                }
            }
        }
    }
}
